#include "include/statistics-module.h"

// Informació del módul
MODULE_AUTHOR("Alex Alvarez Chinchilla & Carlos Garcia Marquez");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TODO");

/*
 * Inicialització
 */

static int __init Statistics_init(void)
{
	// Registrar el manegador
	statistics_dev = MKDEV(STATISTICS_MAJOR, STATISTICS_MINOR);
	register_chrdev_region(statistics_dev, 1, "syscall-monitor");
	statistics_cdev = cdev_alloc();
	statistics_cdev->owner = THIS_MODULE;
	statistics_cdev->ops = &statistics_fops;
	is_open = cdev_add(statistics_cdev, statistics_dev, 1);
	if (is_open < 0) {
		printk(KERN_DEBUG "Error registering module\n");
	} else {
		is_open = 0;
	}
	return is_open;
}

/*
 * Descarrega del módul
 */

static void __exit Statistics_exit(void)
{
	cdev_del(statistics_cdev);
	unregister_chrdev_region(statistics_dev, 1);
	return;
}

/*
 * Funcions d'accés al módul
 */

ssize_t read(struct file *f, char __user *buffer, size_t s, loff_t *off)
{
	struct task_struct * mts;
	struct statistics * mtss;
	int size;
	int result;

	mts = find_task_by_pid(monitored_pid);
	if (mts == 0) {
		return -ESRCH;
	}
	size = sizeof(struct statistics);
	if (size > (int)s) {
		size = s;
	}
	mtss = &((struct my_thread_info*)mts->thread_info)->stats[monitored_call];
	result = copy_to_user(buffer, mtss, size);
	
	return result;
}

int ioctl(struct inode *i, struct file *f, unsigned int arg1, unsigned long arg2)
{
	int pid;
	struct task_struct * mts;
	switch (arg1) {
		case CHANGE_PROCESS:
			if (arg2 == 0) {
				monitored_pid = owner_pid;
			} else {
				pid = *(int*)arg2;
				mts = find_task_by_pid(pid);
				if (mts == 0) {
					return -ESRCH;
				} else {
					monitored_pid = pid;
					if (((struct my_thread_info*)mts->thread_info)->pid != pid) {
						reset_process_statistics(((struct my_thread_info*)mts->thread_info)->stats);
					}
				}
			}
			break;
		case CHANGE_SYSCALL:
			if (arg2 < 0 || arg2 >= NR_CALLS) {
				return -EINVAL;
			} else {
				monitored_call = arg2;
			}
			break;
		case RESET_VALUES:
			mts = find_task_by_pid(monitored_pid);
			if (mts == 0) {
				return -ESRCH;
			} else {
				reset_process_statistics(((struct my_thread_info*)mts->thread_info)->stats);
			}
			break;
		case RESET_ALL_PROCESS_VALUES:
			reset_all_statistics();
			break;
		case ENABLE_SYS_CALL:
			if (arg2 < 0 || arg2 >= NR_CALLS) {
				return -EINVAL;
			} else {
				enable_monitoring((int)arg2);
			}
			break;
		case DISABLE_SYS_CALL:
			if (arg2 < 0 || arg2 >=NR_CALLS) {
				return -EINVAL;
			} else {
				disable_monitoring((int)arg2);
			}
			break;
		default:
			return -EINVAL;
			break;
	}
	return 0;
}

int open(struct inode *i, struct file *f)
{
	if (current->euid != 0) {
		return -EPERM;
	}
	if (is_open) {
		return -EBUSY;
	}
	is_open = 1;
	
	// Monitoritzem, per defecte, el pid que fa l'open i la crida OPEN
	monitored_pid = current->pid;
	monitored_call = OPEN;
	owner_pid = monitored_pid;
	
	return 0;
}

int release(struct inode *i, struct file *f)
{
	is_open = 0;
	return 0;
}

module_init(Statistics_init);
module_exit(Statistics_exit);
